package main

import (
	"fmt"
	"time"
)

func main() {
	//协程
	go fmt.Println("宋发元")
	fmt.Println("我是主main goroutine")
	time.Sleep(time.Second)

	//Channel通道
	//解决多个协程之间的通信
	fmt.Println("******************Channel通道******************")
	//声明一个Channel
	ch := make(chan string)
	//一个chan的操作只有两种：发送和接收。
	go func() {
		fmt.Println("宋发元小哥哥")
		ch <- "goroutine 完成" //发送：向chan发送值，把值放在chan中，操作符为 chan <-
	}()
	fmt.Println("main goroutine")
	v := <-ch //接收：获取chan中的值，操作符为 <- chan
	//小技巧：这里注意发送和接收的操作符，都是 <- ，只不过位置不同。接收的 <- 操作符在 chan 的左侧，发送的 <- 操作符在 chan 的右侧。
	fmt.Println("接收到的chan中的值为：", v)

	//有缓冲Channel
	//一个有缓冲 channel 具备以下特点：
	//1.有缓冲 channel 的内部有一个缓冲队列；
	//2.发送操作是向队列的尾部插入元素，如果队列已满，则阻塞等待，直到另一个 goroutine 执行，接收操作释放队列的空间；
	//3.接收操作是从队列的头部获取元素并把它从队列中删除，如果队列为空，则阻塞等待，直到另一个 goroutine 执行，发送操作插入新的元素。
	fmt.Println("******************有缓冲Channel******************")
	cacheCh := make(chan int, 5)
	cacheCh <- 2
	cacheCh <- 3
	fmt.Println("cacheCh容量为：", cap(cacheCh), "，元素个数为：", len(cacheCh))

	//多路复用select+channel实例
	//多路复用可以简单地理解为，N 个 channel 中，任意一个 channel 有数据产生，select 都可以监听到，然后执行相应的分支，接收数据并处理。
	fmt.Println("******************多路复用select+channel实例******************")
	//声明三个存放结果的Channel
	firstCh := make(chan string)
	secondCh := make(chan string)
	threeCh := make(chan string)
	//同时开启3个goroutine下载
	go func() {
		firstCh <- downloadFile("firstCh")
	}()
	go func() {
		secondCh <- downloadFile("secondCh")
	}()
	go func() {
		threeCh <- downloadFile("threeCh")
	}()
	//开始select多路复用，哪个Channel能获取到值，就说明哪个最先下载好，就用哪个。
	//如果这些 case 中有一个可以执行，select 语句会选择该 case 执行，如果同时有多个 case 可以被执行，则随机选择一个，这样每个 case 都有平等的被执行的机会。如果一个 select 没有任何 case，那么它会一直等待下去。
	select {
	case filePath := <-firstCh:
		fmt.Println(filePath)
	case filePath := <-secondCh:
		fmt.Println(filePath)
	case filePath := <-threeCh:
		fmt.Println(filePath)
	}
}

func downloadFile(chanName string) string {
	//模拟下载文件，可以自己随机time.Sleep点时间试试
	time.Sleep(time.Second)
	return chanName + ":filePath"
}
